#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "mntent.h"

static struct mntent ent;
static char line[1024];

#define BLANKS	" \t"
#define ISBLANKS(x)	((x) == ' ' || (x) == '\t')
#define isdigit(x)	((x) >= '0' && (x) <= '9')

#define SKIP_BLANKS(__tmp)  				\
	do { 						\
		while (*__tmp && ISBLANKS(*__tmp)) 	\
		{					\
			__tmp++;			\
		}					\
		if (!*__tmp)				\
		{					\
			return NULL;			\
		}					\
	} while (0)

#define GET_ONE_SEG(start)				\
	do {						\
		start = strtok(start, BLANKS);		\
		if (!start)				\
		{					\
			return NULL;			\
		}					\
	} while (0)

FILE *setmntent(const char *file, const char *mode)
{
	if (!file || !mode)
	{
		return NULL;
	}

	return fopen(file, mode);
}


		
/* Read one mount table entry from STREAM.  Returns a pointer to storage
   reused on the next call, or null for EOF or error (use feof/ferror to
   check).  */
// how to free the strdup's memory!
struct mntent *getmntent(FILE *stream)
{
	char *p;

	if (!stream)
		return NULL;

	for (;;)
	{
		p = fgets(line, sizeof(line) - 1, stream);
		if (!p)
		{
			return NULL;
		}

		// filesystem
		SKIP_BLANKS(p);
		if (*p == '#')
		{
			continue;
		}

		GET_ONE_SEG(p);
		ent.mnt_fsname = strdup(p);

		// mount dir
		p = NULL;
		GET_ONE_SEG(p);
		ent.mnt_dir = strdup(p);

		// mount type
		p = NULL;
		GET_ONE_SEG(p);
		ent.mnt_type = strdup(p);

		// mount options
		p = NULL;
		GET_ONE_SEG(p);
		ent.mnt_opts = strdup(p);

		// mount frequency
		p = NULL;
		GET_ONE_SEG(p);
		if (!isdigit(*p))
		{
			return NULL;
		}
		ent.mnt_freq = atoi(p);

		// pass no
		p = NULL;
		GET_ONE_SEG(p);
		if (!isdigit(*p))
		{
			return NULL;
		}
		ent.mnt_passno = atoi(p);

		break;
	}

	return &ent;
}


/* Write the mount table entry described by MNT to STREAM.
   Return zero on success, nonzero on failure.  */
int addmntent(FILE *stream,
		const struct mntent *mnt)
{
	char *p;
	int len;

	if (!stream || !mnt)
	{
		return -1;
	}

	p = line;

	snprintf(p, sizeof(line) - 1, "%s\t%s\t%s\t%s\t%d\t%d",
			mnt->mnt_fsname, mnt->mnt_dir,
			mnt->mnt_type, mnt->mnt_opts, 
			mnt->mnt_freq, mnt->mnt_passno);

	if (fseek(stream, 0, SEEK_END) < 0)
	{
		return -1;
	}

	len = strlen(p);

	if (fwrite(p, 1, len, stream) != len)
	{
		return -1;
	}

	return 0;
}

/* Close a stream opened with `setmntent'.  */
int endmntent (FILE *stream)
{
	if (!stream)
	{
		return -1;
	}

	return fclose(stream);
}

/* Search MNT->mnt_opts for an option matching OPT.
   Returns the address of the substring, or null if none found.  */
char *hasmntopt (const struct mntent *mnt,
		const char *opt)
{
	char *str;
	char *p;
	int n;
	int len;

	if (!mnt || !opt)
	{
		return NULL;
	}

	len = strlen(opt);

	str = mnt->mnt_opts;
	p = str;
	for (;;)
	{
		str = p;
		if (*str && *str == ',')
		{
			str++;
		}

		SKIP_BLANKS(str);
		p = str;
		while (*p && (!ISBLANKS(*p) && *p != ','))
		{
			p++;
		}

		n = p - str;
		if (n != len)
			continue;

		if (strncasecmp(opt, str, n) == 0)
		{
			return str;
		}
	}

	return NULL;
}


#if 0
int main(void)
{
	struct mntent *pent;
	struct mntent my_ent;
	FILE *fp;
	char *p;

	fp = setmntent("./fstab", "r+");
	if (!fp)
		return 1;
	while ((pent = getmntent(fp)))
	{
		printf("%s\t%s\t%s\t%s\t%d\t%d\n",
				pent->mnt_fsname, pent->mnt_dir,
				pent->mnt_type, pent->mnt_opts,
				pent->mnt_freq, pent->mnt_passno);

		if ((p = hasmntopt(pent, "noauto")))
		{
			printf("p = %s\n", p);
		}
	}

	my_ent.mnt_fsname = "/dev/test";
	my_ent.mnt_dir = "/test";
	my_ent.mnt_type = "minixfs";
	my_ent.mnt_opts = "readonly";
	my_ent.mnt_freq = 1;
	my_ent.mnt_passno = 1;
	
	if (addmntent(fp, &my_ent) < 0)
	{
		printf("add mnt entry error!\n");
	}

	endmntent(fp);

	return 0;
}
#endif
